<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
               "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SimGear XML Support</title>
</head>

<body bgcolor="white">
<h1>SimGear XML Support: C++ Programmer's Guide</h1>

<p><em>26 July 2000</em></p>

<p>A <em>Public Domain</em> module that comes with <strong>NO
WARRANTY</strong> of any kind.</p>

<dl>
<dt>1. <a href="#synopsis">Synopsis</a></dt>
<dt>2. <a href="#description">Description</a></dt>
<dt>3. <a href="#classes">Classes and Functions</a></dt>
<dd><dl>
<dt>3.1. <a href="#classes.XMLVisitor">The <var>XMLVisitor</var>
Class</a></dt>
<dt>3.2. <a href="#classes.XMLAttributes">The <var>XMLAttributes</var>
Class</a></dt>
<dt>3.3. <a href="#classes.XMLAttributesDefault">The
<var>XMLAttributesDefault</var> Class</a></dt>
<dt>3.4. <a href="#classes.readXML">The <var>readXML</var>
Function</a></dt>
</dl></dd>
<dt>4. <a href="#examples">Examples</a></dt>
<dt>5. <a href="#todo">TODO</a></dt>
<dt>6. <a href="#author">Credits, Author, and Copyright</a></dt>
</dl>

<div><a name="synopsis"></a>
<h2>1. Synopsis</h2>

<p>Declaring a visitor:</p>

<blockquote><pre>
#include &lt;simgear/xml/easyxml.hxx&gt;

class MyVisitor : public XMLDocumentVisitor
{
public:
  // ...
  void startDocument ();
  void endDocument ();
  void startElement (const char * name, const XMLAttributes &amp;atts);
  void endElement (const char * name);
  void data (const char * s, int length);
  void warning (const char * message, int line, int col);
  void error (const char * message, int line, int col);
};
</pre></blockquote>

<p>Reading an XML document:</p>

<blockquote><pre>
MyVisitor visitor;
if (!readXML(&amp;myinput, visitor))
  cerr << "Error reading XML document." << endl;
</pre></blockquote>

<!-- end of "Synopsis" -->
</div>

<div><a name="description"></a>
<h2>2. Description</h2>

<p><em><strong>Note:</strong> EasyXML is a low-level interface that deals
with the actual tags, attributes, and character data in an XML
document.  In most cases, SimGear programmers should use the
<var>readXML</var> and <var>writeXML</var> methods in the
<var>SGPropertyList</var> class, which hide the details of the XML
markup.  Use this interface only if you want to create a new kind of
XML framework.</em></p>

<p>This module provides an interface for XML processing based on the
<cite>visitor</cite> design pattern, as described in by Gamma, Helm,
Johnson, and Vlissides (the <em>Gang of Four</em>) in <cite>Design
Patterns</cite>.  Here's how the processing works:</p>

<ol>
<li>The application writer (that's you) creates a new class that
inherits from <var>XMLVisitor</var>, overriding methods for the events
that interest you.</li>
<li>The application (you, again) creates a C++ input stream for the XML
document.</li>
<li>The application calls <var>readXML</var>, passing the input stream
and an instance of the visitor as parameters.</li>
<li>The XML parser reads through the XML document.  Every time it
finds something significant in the document (the start or end, the
start or end of an element, some data characters, an error, etc.) it
invokes the appropriate method on the visitor.</li>
<li>When the document is completely parsed, the <var>readXML</var>
function returns control to the application with a bool value
indicating whether the document was parsed successfully.</li>
</ol>

<p>In other words, you hand over flow control to the
<var>readXML</var> function, and it hands control on to the various
methods in your visitor as appropriate, then returns control to the
top level of your application after parsing.</p>

<p>While this approach to reading XML may seem a little strange at
first, it has some significant advantages.  First, since EasyXML does
not build an in-memory tree of the entire XML document before
processing, processing is very fast and isn't restricted by memory
(you can process an XML document much larger than available memory).
Second, using a visitor allows you to encapsulate all of your import
logic into a single class: the most typical use for a visitor is to
build an application-specific data structure of some sort, so that
when the XML document processing is finished, the data structure is
completely filled in.  Finally, the use of classes for visitors allows
an advanced application writer to construct a chain of visitors (or
filters) where each one makes a specific set of modifications to the
event stream and then passes the events on to the next visitor for
further processing.</p>

<p>For example, consider the following very simple XML document:</p>

<blockquote><pre>
&lt;?xml version="1.0"?&gt;

&lt;greeting&gt;Hello, &lt;em&gt;world&lt;/em&gt;!&lt;/greeting&gt;
</pre></blockquote>

<p>The <var>readXML</var> function would invoke the following methods
in the visitor:</p>

<table border="border">

<tr>
<th>Method</th>
<th>First Parameter</th>
</tr>

<tr>
<td><code>startXML</code></td>
<td>--</td>
</tr>

<tr>
<td><code>startElement</code></td>
<td><var>greeting</var></td>
</tr>

<tr>
<td><code>data</code></td>
<td>"Hello, "</td>
</tr>

<tr>
<td><code>startElement</code></td>
<td><var>em</var></td>
</tr>

<tr>
<td><code>data</code></td>
<td>"world"</td>
</tr>

<tr>
<td><code>endElement</code></td>
<td><var>em</var></td>
</tr>

<tr>
<td><code>data</code></td>
<td>"!"</td>
</tr>

<tr>
<td><code>endElement</code></td>
<td><var>greeting</var></td>
</tr>

<tr>
<td><code>endXML</code></td>
<td>--</td>
</tr>

</table>

<p>Note that the XML declaration ("&lt;?xml...") <em>never</em>
generates a <var>pi</var> event, since it is not a processing
instruction.</p>

<p>This module does not (yet) support XML Namespaces, though the
underlying Expat parsing engine does.</p>

<!-- end of "Description" -->
</div>


<div><a name="classes"></a>
<h2>3. Classes and Functions</h2>

<p>SimGear's low-level XML support consists of three classes and one
global function:</p>

<ol>

<li>
<p>the <var><a href="#classes.XMLVisitor">XMLVisitor</a></var>
abstract base class, which the application writer extends to handle
XML document events;</p>
</li>

<li>
<p>the <var><a href="#classes.XMLAttributes">XMLAttributes</a></var>
abstract base class, which represents the interface to a list of XML
attributes;</p>
</li>

<li>
<p>the <var><a
href="#classes.XMLAttributesDefault">XMLAttributesDefault</a></var>
class, which provides a default, mutable implementation of an XML
attribute list (and allows the application to make persistent
copies); and</p>
</li>

<li>the <var><a href="#classes.readXML">readXML</a></var> function,
which initiates processing of an XML document.</li>

</ol>


<div><a name="classes.XMLVisitor"></a>
<h3>3.1. The <var>XMLVisitor</var> Class</h3>

<p>The <var>XMLVisitor</var> class itself doesn't actually do
anything; instead, the application writer must extend it and override
methods to catch specific events.  The <var><a
href="#classes.readXML">readXML</a></var> function will invoke the
methods in this class to report events as it parses an XML
document.</p>

<p>Note that all names and character data will be UTF-8 encoded: that
means that non-ASCII characters will be represented by more than one
byte.</p>

<dl>

<dt><code>void startXML ()</code></dt>
<dd>Report the beginning of the XML document.  This method will be
invoked exactly once, and it will always be the first one called.</dd>

<dt><code>void endXML ()</code></dt>
<dd>Report the end of the XML document.  This method will be invoked
exactly once, and it will always be the last method invoked.</dd>

<dt><code>void startElement (const char * name, const XMLAttributes
&amp;atts)</code></dt>
<dd>Report the start of an XML element.  The <var>atts</var> parameter
holds an <var><a href="#classes.XMLAttributes">XMLAttributes</a></var>
object that the application can use to iterate through the element's
attributes or to look up the values of specific attributes.
<strong>Note:</strong> the <var>atts</var> object is valid only until
this method returns; if the application needs to keep a copy of the
attribute list, it must use the <var><a
href="#classes.XMLAttributesDefault">XMLAttributesDefault</a></var>
class to take a snapshot.</dd>

<dt><code>void endElement (const char * name)</code></dt>
<dd>Report the end of an XML element.  This method will always be
invoked, even when an element is represented by an empty tag
(i.e. "&lt;hr/&gt;").  <var>startElement</var> and
<var>endElement</var> invocations will be properly nested.</dd>

<dt><code>void data (const char * s, int length)</code></dt>
<dd>Report character data in the XML document: the <var>s</var>
parameter points to the beginning of a buffer containing the
characters, and the <var>length</var> parameter indicates the buffer's
length.  As required by the XML Recommendation, This method will be
used to report all character data within the document element, even
characters that the document author might have considered to be
whitespace (such as newlines or indentation).  <strong>Note:</strong>
a series of consecutive characters may be broken up into any number of
separate <var>data</var> events, so it is never safe to assume that
all of an element's data content is being reported in a single
event.</dd>

<dt><code>void pi (const char * target, const char * data)</code></dt>
<dd>Report a processing instruction.  Processing instructions are used
occasionally in XML documents, especially for linking stylesheets to
prose documents, but most applications can simply ignore them.
<strong>Note:</strong> the XML declaration is <em>not</em> a
processing instruction and will note be reported through this
method.</dd>

<dt><code>void warning (const char * message, int line, int
col)</code></dt>
<dd>Report a non-fatal warning, with line and column number (if
available).</dd>

<dt><code>void error (const char * message, int line, int
col)</code></dt>
<dd>Report a fatal error, with line and column number (if available).
Subclasses <em>must</em> implement this method.</dd>

</dl>

<!-- end of "The XMLVisitor Class" -->
</div>


<div><a name="classes.XMLAttributes"></a>
<h3>3.2. The <var>XMLAttributes</var> Class</h3>

<p>This class itself cannot be instantiated, but it provides a common
interface for reading XML attribute lists.  If an application needs to
create its own attribute list or keep a persistent snapshot of one, it
can use the <var><a
href="#classes.XMLAttributesDefault">XMLAttributesDefault</a></var>
class.</p>

<p>The <var>XMLAttributes</var> class contains the following
methods:</p>

<dl>

<dt><code>int size () const</code></dt>
<dd>Return the number of attributes in the list.  Note that this
method may be inefficient in some implementations, so it should not be
called repeatedly in a loop.</dd>

<dt><code>const char * getName (int i) const</code></dt>
<dd>Return the name of the attribute in the specified position.</dd>

<dt><code>const char * getValue (int i) const</code></dt>
<dd>Return the value of the attribute in the specified position.</dd>

<dt><code>int findAttribute (const char * name) const</code></dt>
<dd>Return the position of the attribute with the name provided, or -1
if the name does not appear in the list.</dd>

<dt><code>bool hasAttribute (const char * name) const</code></dt>
<dd>Return <var>true</var> if an attribute with the name specified
appears in the list.</dd>

<dt><code>const char * getValue (const char * name) const</code></dt>
<dd>Return the value of the named attribute, or 0 if the name does not
appear in the list.</dd>

</dl>

<!-- end of "The XMLAttributes Class" -->
</div>


<div><a name="classes.XMLAttributesDefault"></a>
<h3>3.3. The <var>XMLAttributesDefault</var> Class</h3>

<p>This class extends <var><a
href="#classes.XMLAttributes">XMLAttributes</a></var> to provide a
default, mutable implement of XML attribute lists.  You can also use
this class to take a persistent snapshot of an attribute list.</p>

<p>The <var>XMLAttributesDefault</var> class contains all of the
methods in <var><a
href="#classes.XMLAttributes">XMLAttributes</a></var>, together with
the following extensions:</p>

<dl>

<dt><code>XMLAttributesDefault ()</code></dt>
<dd>Construct a new, empty attribute list.</dd>

<dt><code>XMLAttributesDefault (const XMLAttributes &amp;atts)</code></dt>
<dd>Take a snapshot of an existing XML attribute list.</dd>

<dt><code>void addAttribute (const char * name, const char * value)</code></dt>
<dd>Add an attribute to the list (this method will not check to see if
the attribute already exists, so use with caution).  The
<var>setValue</var> method provides a safer (but slower)
alternative.</dd>

<dt><code>void setName (int i, const char * name)</code></dt>
<dd>Change the name of the attribute in position <var>i</var>.  This
method does no bounds checking, so use it carefully.</dd>

<dt><code>void setValue (int i, const char * value)</code></dt>
<dd>Change the value of the attribute in position <var>i</var>.  This
method does no bounds checking, so use it carefully.</dd>

<dt><code>void setValue (const char * name, const char * value)</code></dt>
<dd>Change the value of the named attribute if it exists in the list,
and adds the named attribute if it does not already exist.</dd>

</dl>

<!-- end of "The XMLAttributesDefault Class" -->
</div>


<div><a name="classes.readXML"></a>
<h3>3.4. The <var>readXML</var> Function</h3>

<dl>
<dt><code>bool readXML (istream &amp;input, XMLVisitor
&amp;visitor)</code></dt>
<dd>Read an XML document from a C++ input stream, reporting parsing
events to the visitor provided.  Return <var>true</var> if the
document was parsed without errors, and <var>false</var>
otherwise.</dd>
</dl>

<!-- end of "The readXML Function" -->
</div>

<!-- end of "Classes" -->
</div>


<div><a name="examples"></a>
<h2>4. Examples</h2>

<p>The following sample program creates a visitor that simply prints
messages to standard output for each event:</p>

<blockquote><pre>
#include &lt;string>
#include &lt;iostream>
#include &lt;fstream>
#include "easyxml.hxx"

using std::string;
using std::ifstream;
using std::cout;
using std::cerr;
using std::endl;

//
// Define a custom visitor that prints out its events.
//
class MyVisitor : public XMLVisitor
{
public:
  virtual void startXML () {
    cout &lt;&lt; "Start XML" &lt;&lt; endl;
  }
  virtual void endXML () {
    cout &lt;&lt; "End XML" &lt;&lt; endl;
  }
  virtual void startElement (const char * name, const XMLAttributes &amp;atts) {
    cout &lt;&lt; "Start element " &lt;&lt; name &lt;&lt; endl;
    for (int i = 0; i &lt; atts.size(); i++)
      cout &lt;&lt; "  " &lt;&lt; atts.getName(i) &lt;&lt; '=' &lt;&lt; atts.getValue(i) &lt;&lt; endl;
  }
  virtual void endElement (const char * name) {
    cout &lt;&lt; "End element " &lt;&lt; name &lt;&lt; endl;
  }
  virtual void data (const char * s, int len) {
    cout &lt;&lt; "Character data " &lt;&lt; string(s,len) &lt;&lt; endl;
  }
  virtual void pi (const char * target, const char * data) {
    cout &lt;&lt; "Processing instruction " &lt;&lt; target &lt;&lt; ' ' &lt;&lt; data &lt;&lt; endl;
  }
  virtual void warning (const char * message, int line, int column) {
    cout &lt;&lt; "Warning: " &lt;&lt; message &lt;&lt; " (" &lt;&lt; line &lt;&lt; ',' &lt;&lt; column &lt;&lt; ')'
	 &lt;&lt; endl;
  }
  virtual void error (const char * message, int line, int column) {
    cout &lt;&lt; "Error: " &lt;&lt; message &lt;&lt; " (" &lt;&lt; line &lt;&lt; ',' &lt;&lt; column &lt;&lt; ')'
	 &lt;&lt; endl;
  }
};


//
// Process the command line and start the parse.
//
main (int ac, const char ** av)
{
  MyVisitor visitor;

  for (int i = 1; i &lt; ac; i++) {
    ifstream input(av[i]);
    cout &lt;&lt; "Reading " &lt;&lt; av[i] &lt;&lt; endl;
    if (!readXML(input, visitor)) {
      cerr &lt;&lt; "Error reading from " &lt;&lt; av[i] &lt;&lt; endl;
    }
  }
}
</pre></blockquote>

<!-- end of "Examples" -->
</div>


<div><a name="todo"></a>
<h2>5. TODO</h2>

<ul>
<li>Namespace support.</li>
<li>An XMLWriter class (which extends XMLVisitor).</li>
<li>An XMLFilter class (which extends XMLVisitor).</li>
<li>A data mode that is smarter about ignoring whitespace.</li>
<li>(Maybe) a very simple, read-only tree-based API.</li>
</ul>

<!-- end of "TODO" -->
</div>


<div><a name="author"></a>
<h2>6. Credits, Author, and Copyright</h2>

<p>Included with this module are several files from James Clark's
<cite><a href="http://www.jclark.com/xml/expat.html">Expat</a></cite>
XML parser.  These files, as indicated in their headers, are
distributed under the <cite>Mozilla Public License</cite>.</p>

<p>The EasyXML module itself was written by David Megginson (<a
href="mailto:david@megginson.com" >david@megginson.com</a>), and is
hereby released into the Public Domain with no restrictions at all on
its use, commercial or non-commercial.  This module comes with no
warranty at all, and the author assumes no responsibility for its
correct performance, etc. etc. blah blah blah...</p>

<p>The module is in the public domain because copyright of any kind
(even GPL) implies a threat to take legal action against anyone who
violates it.  I have no intention to take any kind of legal action no
matter what people do with this code, and I don't believe in making
empty threats.</p>

<p>Because the module is public domain, you are free to switch it to
whatever license you want to use in your program, free or otherwise
(note again that this does not apply to the files taken from Expat).</p>

<!-- end of "Author" -->
</div>

<hr />

<address>$Id: easyxml.html,v 1.1.1.1 2002/09/07 02:58:20 curt Exp $</address>

</body>
</html>
